package com.zhengqiangblog.blog.dao.common;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zhengqiangblog.core.RowMapper;

public class JdbcTemplate {

	private static final Logger logger = Logger.getLogger(JdbcTemplate.class);

	private ComboPooledDataSource dataSource = DataSourceFactory.getDataSource();

	public JdbcTemplate() {
		logger.info("JdbcTemplate initializing...");
	}

	public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) throws Exception {
		Connection connection = null;
		List<T> result = new ArrayList<T>();
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(sql);
			bindArguments(pstmt, args);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				int rowNum = rs.getRow();
				T item = rowMapper.mapRow(rs, rowNum);
				result.add(item);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			closeAll(connection, pstmt, rs);
		}
		return result;
	}

	public final <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws Exception {
		List<T> list = this.query(sql, rowMapper, args);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	public final int update(String sql, Object... args) throws Exception {
		int effectedRows = 0;
		Connection connection = null;
		PreparedStatement pstmt = null;
		try {
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(sql);
			bindArguments(pstmt, args);
			effectedRows = pstmt.executeUpdate();
		} catch (Exception e) {
			throw e;
		} finally {
			closeAll(connection, pstmt, null);
		}
		return effectedRows;
	}

	public final List<Object> saveAndFetchGeneratedKeys(String sql, Object... args) throws Exception {
		List<Object> generatedKeys = new ArrayList<Object>();
		Connection connection = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			connection = dataSource.getConnection();
			pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			bindArguments(pstmt, args);
			pstmt.executeUpdate();
			rs = pstmt.getGeneratedKeys();
			while (rs.next()) {
				generatedKeys.add(rs.getObject(1));
			}
		} catch (Exception e) {
			throw e;
		} finally {
			closeAll(connection, pstmt, rs);
		}
		return generatedKeys;
	}

	private final void closeAll(Connection connection, Statement stmt, ResultSet rs) throws Exception {
		if (rs != null) {
			rs.close();
		}
		if (stmt != null) {
			stmt.close();
		}
		if (connection != null) {
			connection.close();
		}
	}

	private final void bindArguments(PreparedStatement pstmt, Object... args) throws Exception {
		if (args != null && args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				Object param = args[i];
				int paramIndex = i + 1;
				if (param instanceof Integer) {
					pstmt.setInt(paramIndex, (Integer) param);
				} else if (param instanceof Long) {
					pstmt.setLong(paramIndex, (Long) param);
				} else if (param instanceof Date) {
					pstmt.setTimestamp(paramIndex, new Timestamp(((Date) param).getTime()));
				} else {
					pstmt.setString(paramIndex, (String) param);
				}
			}
		}
	}

}
